package com.gxtbindings.bindings.client.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import com.gxtbindings.bindings.client.util.ListChangeEvent.ListChangeType;

/**
 * A java.util.List which has change support to notify listeners whenever the list is changed.
 * 
 * @author Jonathan Jonathan
 *
 */
public class EventList<E> implements List<E> {

	private List<E> list;
	
	private List<EventListListener<E>> listeners = new ArrayList<EventListListener<E>>();
	
	public EventList()
	{
		list = new ArrayList<E>();
	}
	
	public EventList(Collection< ? extends E> c)
	{
		list = new ArrayList<E>(c);
	}
	
	public EventList(int initialCapacity)
	{
		list = new ArrayList<E>(initialCapacity);
	}
	
	public void addEventListListener(EventListListener<E> listener)
	{
		listeners.add(listener);
	}
	
	public void removeEventListListener(EventListListener<E> listener)
	{
		listeners.remove(listener);
	}
	
	private void fireInsertEvent(int startIndex, int endIndex)
	{
		fireEvent(new ListChangeEvent<E>(ListChangeType.INSERT, startIndex, endIndex, this));
	}
	
	private void fireDeleteEvent(int startIndex, int endIndex)
	{
		fireEvent(new ListChangeEvent<E>(ListChangeType.DELETE, startIndex, endIndex, this));
	}
	
	private void fireEvent(ListChangeEvent<E> ev)
	{
		for (EventListListener<E> listener : listeners)
		{
			listener.listChanged(ev);
		}
	}
	
	public boolean add(E e) {
		boolean val = list.add(e);
		
		if (val)
		{
			fireInsertEvent(list.size()-1, list.size());
		}
		return val;
	}

	@Override
	public void add(int index, E element) {
		list.add(index, element);
		fireInsertEvent(index, index+1);
	}

	@Override
	public boolean addAll(Collection<? extends E> c) {
		boolean val = list.addAll(c);
		if (val)
		{
			fireInsertEvent(list.size()-c.size(), list.size());
		}
		return val;
	}

	@Override
	public boolean addAll(int index, Collection<? extends E> c) {
		boolean val = list.addAll(index, c);
		if (val)
		{
			fireInsertEvent(index, index+c.size());
		}
		return val;
	}

	public void clear() {
		int size = list.size();
		list.clear();
		fireDeleteEvent(0, size);
	}

	public boolean contains(Object arg0) {
		return list.contains(arg0);
	}

	public boolean containsAll(Collection<?> arg0) {
		return list.containsAll(arg0);
	}

	public E get(int arg0) {
		return list.get(arg0);
	}

	public int indexOf(Object arg0) {
		return list.indexOf(arg0);
	}

	public boolean isEmpty() {
		return list.isEmpty();
	}

	public Iterator<E> iterator() {
		return list.iterator();
	}

	public int lastIndexOf(Object arg0) {
		return list.lastIndexOf(arg0);
	}

	public ListIterator<E> listIterator() {
		return list.listIterator();
	}

	public ListIterator<E> listIterator(int arg0) {
		return list.listIterator(arg0);
	}

	public boolean remove(Object arg0) {
		int index = list.indexOf(arg0);
		boolean val = list.remove(arg0);
		if (val)
		{
			fireDeleteEvent(index, index+1);
		}
		return val;
	}

	public E remove(int index) {
		E obj = list.remove(index);
		fireDeleteEvent(index, index+1);
		return obj;
	}

	public boolean removeAll(Collection<?> c) {
		boolean changed = false;
		
		for (Object obj : c)
		{
			int index = list.indexOf(obj);
			if (index >= 0)
			{
				list.remove(index);
				changed = true;
				fireDeleteEvent(index, index+1);
			}
		}
		return changed;
	}

	public boolean retainAll(Collection<?> c) {
		boolean changed = false;
		{
			int index = 0;
			for (E obj : list)
			{
				if (!c.contains(obj))
				{
					list.remove(index);
					changed = true;
					fireDeleteEvent(index, index+1);
				}
				index++;
			}
		}
		return changed;
	}

	@Override
	public E set(int index, E element) {
		E obj = list.set(index, element);
		fireEvent(new ListChangeEvent<E>(ListChangeType.UPDATE, index, index+1, this));
		return obj;
	}

	public int size() {
		return list.size();
	}

	public List<E> subList(int fromIndex, int toIndex) {
		return list.subList(fromIndex, toIndex);
	}

	public Object[] toArray() {
		return list.toArray();
	}

	public <T> T[] toArray(T[] arg0) {
		return list.toArray(arg0);
	}
}
